package edu.bbu.license.client.android.services.file.worker;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import android.os.Environment;
import edu.bbu.license.client.android.communication.exceptions.DataBaseErrorException;
import edu.bbu.license.client.android.communication.exceptions.DirectoryOperationException;
import edu.bbu.license.client.android.communication.exceptions.FileOperationException;
import edu.bbu.license.client.android.communication.exceptions.NoConnectionException;
import edu.bbu.license.client.android.communication.factory.CLTFactory;
import edu.bbu.license.client.android.communication.model.DirectoryPOJO;
import edu.bbu.license.client.android.communication.model.FilePOJO;
import edu.bbu.license.client.android.communication.services.DirectoryCLTServices;
import edu.bbu.license.client.android.communication.services.FileCLTServices;
import edu.bbu.license.client.android.services.BaseService;
import edu.bbu.license.client.android.services.exceptions.DuplicationException;
import edu.bbu.license.client.android.services.exceptions.FormatException;
import edu.bbu.license.client.android.services.exceptions.IdentificationException;
import edu.bbu.license.client.android.services.exceptions.NetworkException;
import edu.bbu.license.client.android.services.exceptions.UnknownException;
import edu.bbu.license.client.android.services.file.model.LocalFile;
import edu.bbu.license.client.android.services.file.model.ServerFile;
import edu.bbu.license.client.android.services.file.worker.util.FileNameComparator;

public class SimpleFileService extends BaseService implements FileService {
	
	private String[] ReservedChars = {"|", "\\", "?", "*", "<", "\"", ":", ">", "/"};
	
	private FileCLTServices fileServices = CLTFactory.getCLTFactory(CLTFactory.THRIFT_FACTORY).getFileCLTServices();
	private DirectoryCLTServices directoryServices =  CLTFactory.getCLTFactory(CLTFactory.THRIFT_FACTORY).getDirectoryCLTServices();	
	
	@Override
	public LocalFile getLocalRootDirectory() throws IdentificationException {
		if(isUserLoggedIn()) {
			File externalStorrage = Environment.getExternalStorageDirectory();
			
			if(externalStorrage.getParent() != null) {
				return new LocalFile(externalStorrage.getParent());
			} else {
				return new LocalFile(externalStorrage);
			}
		} else {
			throw new IdentificationException("Getting the local root directory failed, user isn't logged in.");
		}
	}

	@Override
	public List<LocalFile> getLocalDirectoryContent(LocalFile directory) throws IdentificationException {
		if(isUserLoggedIn()) {
			File directoryFile = directory.getRegularFile();
			
			LinkedList<LocalFile> files = new LinkedList<LocalFile>();
			LinkedList<LocalFile> directoryes = new LinkedList<LocalFile>();
			FileNameComparator comparator = new FileNameComparator();
			
			if(directoryFile.isDirectory()) {
				for (File file : directoryFile.listFiles()) {
					if(file.canRead() && file.canWrite() && !file.isHidden()) {
						if(file.isDirectory()) {
							directoryes.add(new LocalFile(file));
						} else {
							files.add(new LocalFile(file));
						}
					}
				}
			}
			
			Collections.sort(files, comparator);
			Collections.sort(directoryes, comparator);
			
			directoryes.addAll(files);
			return directoryes;
		} else {
			throw new IdentificationException("Getting the local directory content failed, user isn't logged in.");
		}
	}
	
	@Override
	public ServerFile getServerRootDirectory() throws IdentificationException, NetworkException {
		if(isUserLoggedIn()) {
			try {
				DirectoryPOJO directory = directoryServices.getBaseDirectory(getCurrentlyLoggedInUser());
				return new ServerFile(directory.getParrentDirectory(), directory.getDirectoryName(), ServerFile.DIRECTORY);
			} catch (NoConnectionException e) {
				throw new NetworkException("Getting the servers root directory failed, network error.", e);
			}
		} else {
			throw new IdentificationException("Getting the servers root directory failed, user isn't logged in.");
		} 
	}
	
	@Override
	public List<ServerFile> getServerDirectoryContent(ServerFile directory) throws IdentificationException, NetworkException {
		if(isUserLoggedIn()) {
			if(directory.isDirectory()) {
				LinkedList<ServerFile> files = new LinkedList<ServerFile>();
				LinkedList<ServerFile> directoryes = new LinkedList<ServerFile>();
				
				DirectoryPOJO directoryPOJO = new DirectoryPOJO();
				directoryPOJO.setOwner(getCurrentlyLoggedInUser());
				directoryPOJO.setDirectoryName(directory.getName());
				directoryPOJO.setParrentDirectory(directory.getRegularPath());
				
				try {					
					ArrayList<String> fileNames = fileServices.getFilesFromDirectory(directoryPOJO);
					ArrayList<String> directoryNames = directoryServices.getDirectoriesFromDirectory(directoryPOJO);
					
					for (String fileName : fileNames) {
						files.add(new ServerFile(directory.getAbsolutePath(), fileName, ServerFile.FILE));
					}
					
					for (String direactoryName : directoryNames) {
						directoryes.add(new ServerFile(directory.getAbsolutePath(), direactoryName, ServerFile.DIRECTORY));
					}
				} catch (Exception e) {	
					throw new NetworkException("Getting the directory content failed, network error.", e);
				} 
				
				FileNameComparator comparator = new FileNameComparator();
				Collections.sort(files, comparator);
				Collections.sort(directoryes, comparator);
				
				directoryes.addAll(files);
				return directoryes;
			} else {
				return new LinkedList<ServerFile>();
			}
		} else {
			throw new IdentificationException("Getting the server directory content failed, user isn't logged in.");
		}
	}
	
	@Override
	public List<ServerFile> getServerFileVersionsContent(ServerFile file) throws IdentificationException, NetworkException {
		if(isUserLoggedIn()) {
			if(file.isFile()) {
				LinkedList<ServerFile> fileVersions = new LinkedList<ServerFile>();
				
				FilePOJO filePOJO = new FilePOJO();
				filePOJO.setOwner(getCurrentlyLoggedInUser());
				filePOJO.setFileName(file.getName());
				filePOJO.setDirectoryName(file.getRegularPath());
				
				try {
					ArrayList<Integer> versions = fileServices.getAllFileVersions(filePOJO);
					
					for (Integer version : versions) {
						fileVersions.add(new ServerFile(file.getRegularPath(), file.getName(), version, ServerFile.FILE_VERSION));
					}
				} catch (FileOperationException e) {
					return new LinkedList<ServerFile>();
				} catch (Exception e) {
					throw new NetworkException("Getting the server file versions content failed, network error.", e);
				}
				
				return fileVersions;
			} else {
				return new LinkedList<ServerFile>();
			}
		} else {
			throw new IdentificationException("Getting the server file versions content failed, user isn't logged in.");
		}
	}
	
	@Override
	public void createLocalDirectory(LocalFile directory) throws IdentificationException, FormatException, DuplicationException, UnknownException {
		if(isUserLoggedIn()) {
			File directoryFile = directory.getRegularFile();
			
			for (String character : ReservedChars) {
				if(directoryFile.getName().contains(character)) {
					throw new FormatException("Creating new local directory failed, directory name contains reserved charaters.");
				}
			}
			
			if(directoryFile.exists() && directoryFile.isDirectory()) {
				throw new DuplicationException("Creating new local directory failed, directory already exits.");
			}
			
			boolean success = directoryFile.mkdir();
			
			if(!success) {
				throw new UnknownException("Creating new local directory failed, unknown error.");
			}
		} else {
			throw new IdentificationException("Creating new local directory failed, user isn't logged in.");
		}
	}
	
	@Override
	public void createServerDiretory(ServerFile directory) throws IdentificationException, FormatException, DuplicationException, UnknownException, NetworkException {
		if(isUserLoggedIn()) {
			for (String character : ReservedChars) {
				if(directory.getName().contains(character)) {
					throw new FormatException("Creating new server directory failed, directory name contains reserved charaters.");
				}
			}
			
			DirectoryPOJO directoryPOJO = new DirectoryPOJO();
			directoryPOJO.setOwner(getCurrentlyLoggedInUser());
			directoryPOJO.setDirectoryName(directory.getName());
			directoryPOJO.setParrentDirectory(directory.getRegularPath());
			
			try {
				directoryServices.createDirectory(directoryPOJO);
			} catch (NoConnectionException e) {
				throw new NetworkException("Creating new server directory failed, network error.", e);
			} catch (DirectoryOperationException e) {
				throw new DuplicationException("Creating new server directory failed, directory already exits.", e);
			} catch (DataBaseErrorException e) {
				throw new UnknownException("Creating new server directory failed, unknown error.", e);
			}
		} else {
			throw new IdentificationException("Creating new server directory failed, user isn't logged in.");
		}
	}
	
	@Override
	public void deleteLocalFile(LocalFile file) throws IdentificationException, UnknownException {
		if(isUserLoggedIn()) {
			File regularFile = file.getRegularFile();
			
			if(regularFile.isFile()) {
				regularFile.delete();
			} else if(regularFile.isDirectory()) {
				File[] childFiles = regularFile.listFiles();
				
				if(childFiles != null) {
					for (File childFile : childFiles) {
						deleteLocalFile(new LocalFile(childFile));
					}
				}
				
				boolean success = regularFile.delete();
				
				if(!success) {
					throw new UnknownException("Deleting the local file failed, unknown error.");
				}
			}
		} else {
			throw new IdentificationException("Deleting the local file failed, user isn't logged in.");
		}
	}
	
	@Override
	public void deleteServerFile(ServerFile file) throws IdentificationException, NetworkException, UnknownException {
		if(isUserLoggedIn()) {
			try {
				if(file.isDirectory()) {
					DirectoryPOJO directoryPOJO = new DirectoryPOJO();
					directoryPOJO.setOwner(getCurrentlyLoggedInUser());
					directoryPOJO.setDirectoryName(file.getName());
					directoryPOJO.setParrentDirectory(file.getRegularPath());

					ArrayList<String> directoryNames = directoryServices.getDirectoriesFromDirectory(directoryPOJO);
					
					for (String directoryName : directoryNames) {
						deleteServerFile(new ServerFile(file.getAbsolutePath(), directoryName, ServerFile.DIRECTORY));
					}
					
					ArrayList<String> fileNames = fileServices.getFilesFromDirectory(directoryPOJO);
					
					for(String fileName : fileNames) {
						deleteServerFile(new ServerFile(file.getAbsolutePath(), fileName, ServerFile.FILE));
					}
					
					directoryServices.deleteDirectory(directoryPOJO);
				} else if(file.isFileVersion()) {
					FilePOJO filePOJO = new FilePOJO();
					filePOJO.setOwner(getCurrentlyLoggedInUser());
					filePOJO.setFileName(file.getRegularName());
					filePOJO.setVersion(file.getVersion());
					filePOJO.setDirectoryName(file.getRegularPath());
					
					fileServices.deleteFile(filePOJO);
				} else if(file.isFile()) {
					FilePOJO filePOJO = new FilePOJO();
					filePOJO.setOwner(getCurrentlyLoggedInUser());
					filePOJO.setFileName(file.getName());
					filePOJO.setDirectoryName(file.getRegularPath());
					
					fileServices.deleteAllFileVersions(filePOJO);
				}
			} catch (NoConnectionException e) {
				throw new NetworkException("Deleting the server file failed, network error.", e);
			} catch (Exception e) {
				throw new UnknownException("Deleting the server file failed, unknown error.", e);
			} 
		} else {
			throw new IdentificationException("Deleting the server file failed, user isn't logged in.");
		}
	}
}
